package merisis.sense.impl;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.namespace.QName;

import merisis.sense.SenseMaker;

import com.google.inject.Inject;

import procilege.lang.ScopeBlock;

public class SenseMakerImpl implements SenseMaker {

	private long currentno = 1;

	private Map<Long, NameData> snMap = new HashMap<Long, NameData>();

	private Map<NameData, Long> qnameMap = new HashMap<NameData, Long>();

	@Inject
	private Logger logger;

	public long sense(QName qn) {

		if (qn == null)
			throw new IllegalArgumentException("the qualified name is null");

		return newNameInstance(qn.getNamespaceURI(), qn.getLocalPart(), null);
	}

	public QName introspect(long name) {

		return findQNameById(name);
	}

	public long sense() {

		NameData qn = new NameData();

		Long sno = null;
		synchronized (this) {
			sno = qnameMap.get(qn);
			if (sno == null) {
				sno = currentno++;
				qn.namespaceURI = "DEFAULT";
				qn.localpart = "#" + sno;//Long.toHexString(sno);
				snMap.put(sno, qn);
				qnameMap.put(qn, sno);

				if (logger.isLoggable(Level.FINEST)) {
					logger.finest("sensed: " + sno);
				}
			}
		}

		return sno;
	}

	private long newNameInstance(String nsURI, String localpart,
			ScopeBlock orign) {

		NameData qn = new NameData();
		qn.namespaceURI = nsURI;
		qn.localpart = localpart;

		Long sno = null;
		synchronized (this) {
			sno = qnameMap.get(qn);
			if (sno == null) {
				sno = currentno++;
				snMap.put(sno, qn);
				qnameMap.put(qn, sno);

				if (logger.isLoggable(Level.FINEST)) {
					logger.finest(String.format("sensed: %d, '{%s}%s'", sno,
							nsURI, localpart));
				}
			}
		}

		return sno;
	}

	private QName findQNameById(long sn) {

		NameData qn = snMap.get(sn);
		if (qn == null)
			return null;

		return new QName(qn.namespaceURI, qn.localpart);
	}

	private class NameData {

		String namespaceURI;

		String localpart;

		@Override
		public int hashCode() {
			final int PRIME = 31;
			int result = 1;
			result = PRIME * result
					+ ((localpart == null) ? 0 : localpart.hashCode());
			result = PRIME * result
					+ ((namespaceURI == null) ? 0 : namespaceURI.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			final NameData other = (NameData) obj;
			if (localpart == null) {
				if (other.localpart != null)
					return false;
			} else if (!localpart.equals(other.localpart))
				return false;
			if (namespaceURI == null) {
				if (other.namespaceURI != null)
					return false;
			} else if (!namespaceURI.equals(other.namespaceURI))
				return false;
			return true;
		}
	}
}